<?php

namespace app\utils\Service\Tencent;


use Qcloud\Cos\Client;
use QCloud\COSSTS\Sts;

class CosService
{
    private $config;

    public function __construct()
    {
        $this->config = get_addon_config('cos');
    }

    public function getToken()
    {
        $config = $this->stsToken();
        $uploadUrl = $this->config['uploadurl'];//上传域名因存储区域不同而节点不同
        $cdnUrl    = $this->config['cdnurl'];//上传域名因存储区域不同而节点不同
        $appId     = $this->config['appId'] ?? '';
        $bucket    = $this->config['bucket'] ?? '';//存储桶，格式：BucketName-APPID
        $region    = $this->config['region'] ?? '';
        $key       = str_replace('-'.$appId,'',$bucket);//对象在存储桶中的位置，即对象键

        $tmpSecretId = $config['credentials']['tmpSecretId']; //使用临时密钥需要填入，临时密钥生成和使用指引参见https://cloud.tencent.com/document/product/436/14048
        $tmpSecretKey = $config['credentials']['tmpSecretKey']; //使用临时密钥需要填入，临时密钥生成和使用指引参见https://cloud.tencent.com/document/product/436/14048
        $tmpToken = $config['credentials']['sessionToken']; //使用临时密钥需要填入，临时密钥生成和使用指引参见https://cloud.tencent.com/document/product/436/14048
        $cosClient = new Client(
            array(
                'region' => $region,
                'scheme' => 'https', //协议头部，默认为 http
                'signHost' => true, //默认签入Header Host；您也可以选择不签入Header Host，但可能导致请求失败或安全漏洞,若不签入host则填false
                'credentials'=> array(
                    'secretId'  => $tmpSecretId,
                    'secretKey' => $tmpSecretKey,
                    'token' => $tmpToken)));


        ### 简单上传预签名
        try {
            $signedUrl = $cosClient->getPreSignedUrl('putObject', array(
                'Bucket' => $bucket, //存储桶，格式：BucketName-APPID
                'Key' => $key, //对象在存储桶中的位置，即对象键
                'Body' => 'string',
                'Params'=> array(), //http 请求参数，传入的请求参数需与实际请求相同，能够防止用户篡改此HTTP请求的参数,默认为空
                'Headers'=> array(), //http 请求头部，传入的请求头部需包含在实际请求中，能够防止用户篡改签入此处的HTTP请求头部,默认已签入host
            ), '+10 minutes'); //签名的有效时间
            // 请求成功
            // 请求成功
            return [true,[
                'bucket' => $bucket,
                'region' => $region,
                'uploadUrl' => $uploadUrl,
                'cdnUrl' => $cdnUrl,
                'signUrl' => $signedUrl,
            ]];
        } catch (\Exception $e) {
            // 请求失败
            return [false,$e->getMessage()];
        }
    }

    /**
     * 腾讯云预签名URL
     */
    public function getSignedUrl($dir = 'uploads/')
    {
        $uploadUrl = $this->config['uploadurl'];//上传域名因存储区域不同而节点不同
        $cdnUrl    = $this->config['cdnurl'];//上传域名因存储区域不同而节点不同
        $secretId  = $this->config['secretId'] ?? '';
        $secretKey = $this->config['secretKey'] ?? '';
        $bucket    = $this->config['bucket'] ?? '';
        $region    = $this->config['region'] ?? '';
        $time      = time();
//        $cosClient = new Client([
//            'region'      => $region,
//            'schema'      => $ssl, //协议头部，默认为http
//            'signHost'    => true, //默认签入Header Host；您也可以选择不签入Header Host，但可能导致请求失败或安全漏洞,若不签入host则填false
//            'credentials' => [
//                'secretId'  => $secretId,
//                'secretKey' => $secretKey
//            ]
//        ]);
        try {
//            $signedUrl = $cosClient->getPreSignedUrl('putObject', [
//                'Bucket'  => $bucket, //存储桶，格式：BucketName-APPID
//                'Key'     => $dir, //对象在存储桶中的位置，即对象键
//                'Body'    => 'string',
//                'Params'  => [], //http 请求参数，传入的请求参数需与实际请求相同，能够防止用户篡改此HTTP请求的参数,默认为空
//                'Headers' => [], //http 请求头部，传入的请求头部需包含在实际请求中，能够防止用户篡改签入此处的HTTP请求头部,默认已签入host
//            ], '+10 minutes'); //签名的有效时间

            //设置该policy超时时间是10s. 即这个policy过了这个有效时间，将不能访问。
            $addTime = 30 * 60;//三十分钟
            $expiration = $this->gmt_iso8601($time + $addTime);

            $StartTimestamp = $time;
            $EndTimestamp = $StartTimestamp + $addTime;
            $KeyTime = "$StartTimestamp;$EndTimestamp";

            $conditions = [
                ['content-length-range', 0, 1048576000],//最大文件大小.用户可以自己设置
                ["bucket" => $bucket],
                ["starts-with", '$key', $dir],// 表示用户上传的数据，必须是以$dir开始，不然上传会失败，这一步不是必须项，只是为了安全起见，防止用户通过policy上传到别人的目录。
                ["q-sign-algorithm" => "sha1"],
                ["q-ak" => $secretId],
                ["q-sign-time" => $KeyTime]
            ];
            $arr    = ['expiration' => $expiration, 'conditions' => $conditions];
            $policy = json_encode($arr);
            $policyBase64 = base64_encode($policy);

            // 签名
            $SignKey = hash_hmac('sha1', $KeyTime, $secretKey);
            $StringToSign = sha1($policy);
            $Signature = hash_hmac('sha1', $StringToSign, $SignKey);

            // 请求成功
            return [true, [
                'upload_url' => $uploadUrl,
                'cdn_url' => $cdnUrl,
                'region' => $region,
                'policy' => $policyBase64,
                'q_sign_algorithm' => 'sha1',
                'q_ak' => $secretId,
                'q_key_time' => $KeyTime,
                'q_signature' => $Signature,
            ]];
        } catch (\Exception $e) {
            return [false, $e->getMessage()];
        }
    }

    public function stsToken()
    {
        $uploadUrl = $this->config['uploadurl'];//上传域名因存储区域不同而节点不同
        $cdnUrl    = $this->config['cdnurl'];//上传域名因存储区域不同而节点不同
        $secretId  = $this->config['secretId'] ?? '';
        $secretKey = $this->config['secretKey'] ?? '';
        $bucket    = $this->config['bucket'] ?? '';
        $region    = $this->config['region'] ?? '';

        $sts = new Sts();
        $config = array(
            'url' => 'https://sts.tencentcloudapi.com/', // url和domain保持一致
            'domain' => 'sts.tencentcloudapi.com', // 域名，非必须，默认为 sts.tencentcloudapi.com
            'proxy' => '',
            'secretId' => $secretId, // 固定密钥,若为明文密钥，请直接以'xxx'形式填入，不要填写到getenv()函数中
            'secretKey' => $secretKey, // 固定密钥,若为明文密钥，请直接以'xxx'形式填入，不要填写到getenv()函数中
            'bucket' => $bucket,//'test-1253653367', // 换成你的 bucket
            'region' => $region,//'ap-guangzhou', // 换成 bucket 所在园区
            'durationSeconds' => 1800, // 密钥有效期
            'allowPrefix' => array('exampleobject.jpg','exampleobject.png','exampleobject/*'), // 这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径，例子： a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
            // 密钥的权限列表。简单上传和分片需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
            'allowActions' => array (
                // 简单上传
                'name/cos:PutObject',
                'name/cos:PostObject',
                // 分片上传
                'name/cos:InitiateMultipartUpload',
                'name/cos:ListMultipartUploads',
                'name/cos:ListParts',
                'name/cos:UploadPart',
                'name/cos:CompleteMultipartUpload'
            ),
            // 临时密钥生效条件，关于condition的详细设置规则和COS支持的condition类型可以参考 https://cloud.tencent.com/document/product/436/71306
//            "condition" => array(
//                "ip_equal" => array(
//                    "qcs:ip" => array(
//                        "10.217.182.3/24",
//                        "111.21.33.72/24",
//                    )
//                )
//            )
        );

// 获取临时密钥，计算签名
        $tempKeys = $sts->getTempKeys($config);
        return $tempKeys;
    }

    private function xml_parser($str)
    {
        $xml_parser = xml_parser_create();
        if (!xml_parse($xml_parser, $str, true)) {
            xml_parser_free($xml_parser);
            return $str;
        } else {
            $res = json_decode(json_encode(simplexml_load_string($str, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
            return $res['Message'] ?? '请求错误';
        }
    }


    /**
     * @param $time
     * @return string
     * @throws \Exception
     */
    private function gmt_iso8601($time)
    {
        $dtStr      = date("c", $time);
        $dateTime   = new \DateTime($dtStr);
        $expiration = $dateTime->format(\DateTime::ISO8601);
        $pos        = strpos($expiration, '+');
        $expiration = substr($expiration, 0, $pos);
        return $expiration . "Z";
    }
}